package com.hyacinth.chache;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * 使用
 *
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class SafeConcurrentHashCacheByFuture<K, V> implements ConcurrentMapCache<K, V>{

    private final ConcurrentHashMap<K, Future<V>> cacheMap = new ConcurrentHashMap<K, Future<V>>();

    public Object getCache(K keyValue, String ThreadName) {
        Future<V> value = null;
        try {
            System.out.println("ThreadName getCache==============" + ThreadName);
            //从缓存获取数据
            value = cacheMap.get(keyValue);
            //如果没有的话，把数据放到缓存
            if (value == null) {
                value = putCache(keyValue, ThreadName);
            }
            return value.get();

        } catch (Exception ignored) {
        }
        return null;
    }

    /**
     * 线程T1或者线程T2访问cacheMap，如果都没有时，这时执行了FutureTask来完成异步任务，
     * 假如线程T1执行了FutureTask，并把保存到ConcurrentHashMap中，通过PutIfAbsent方法，
     * 因为putIfAbsent方法如果不存在key对应的值，则将value以key加入Map，否则返回key对应的旧值。
     * 这时线程T2进来时可以获取Future对象，如果没值没关系，这时是对象的引用，
     * 等FutureTask执行完，在通过get返回
     * @param keyValue
     * @param ThreadName
     * @return
     */
    public Future<V> putCache(K keyValue, final String ThreadName) {
		//把数据放到缓存
        Future<V> value = null;
        Callable<V> callable = new Callable<V>() {
            @SuppressWarnings("unchecked")
            @Override
            public V call() throws Exception {
                //可以根据业务从数据库获取等取得数据,这边就模拟已经获取数据了
                System.out.println("ThreadName 执行业务数据并返回处理结果的数据（访问数据库等）==============" + ThreadName);
                return (V) "dataValue";
            }
        };
        FutureTask<V> futureTask = new FutureTask<V>(callable);
        // putIfAbsent方法如果不存在key对应的值，则将value以key加入Map，否则返回key对应的旧值。
        value = cacheMap.putIfAbsent(keyValue, futureTask);
        if (value == null) {
            value = futureTask;
            futureTask.run();
        }
        return value;
    }
}
